package model
{
	import flash.events.*
	import flash.media.ID3Info;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.media.SoundTransform;
	import flash.net.URLRequest;
	import flash.utils.Timer;
	import mx.core.IMXMLObject;
	import mx.rpc.events.AbstractEvent;
	import mx.collections.ArrayCollection;
	import mx.effects.Pause;
	


	[Event(name=PlayerEvent.MediaOpen, type="model.PlayerEvent")]//event sound file play begans..occurs also on pause->play 
	[Event(name=PlayerEvent.Stop, type="model.PlayerEvent")]//event player stop
	[Event(name=PlayerEvent.Pause, type="model.PlayerEvent")]//event player pause
	[Event(name=PlayerEvent.MediaLoadComplete, type="model.PlayerEvent")]//event sound file load complete
	[Event(name=PlayerEvent.IOError, type="model.PlayerEvent")]//event sound file load begans
	[Event(name=PlayerEvent.Bufferring, type="flash.events.ProgressEvent")]//event sound file load progress,buffering...
	[Event(name=PlayerEvent.MediaEnd, type="model.PlayerEvent")]//event sound file plays over
	[Event(name=PlayerEvent.TimeTick, type="model.PlayerEvent")]//event sound file plays time tick,every sec occurs
	

	public class PlayerModel extends EventDispatcher
	{
			private var _engine:Sound;
			private var _channel:SoundChannel;
			private var	_playLists:ArrayCollection; /*array for store play lists*/
			public function get playLists():ArrayCollection{
				return this._playLists;
			}
			private var _currentListIndex:int; /*index determine the which play list for using now*/
			private var _currentSongIndex:int; /*index determine that which song is playing*/
			private var _tickTimer:Timer;
			private var _engineState:String = PlayerState.STOP;
			private var _lastPosition:Number = 0;
					
			public function PlayerModel(){
					
				this._engine = new Sound();
				this._channel = new SoundChannel();
				this._playLists = new ArrayCollection();
				this._currentListIndex = -1;
				this._currentSongIndex = -1;
			}
			
			private var _volume:Number = 0.75;
			public function get volume():Number
			{
				return this._volume;
			}
			public function set volume(value:Number):void
			{
				if (this.volume != value)
				{
					this._volume = value;
		
					if (this._channel)
						this._channel.soundTransform = new SoundTransform(this.volume);
		
					var evt:PlayerEvent = new PlayerEvent(PlayerEvent.VolumeChange,this.state);
					evt.playerTimeState = this.timeState;
					dispatchEvent(evt);
				}
			}
			public function get position():Number
			{
				return this._channel?this._channel.position:this._lastPosition;
			}
			public function set position(value:Number):void
			{
				if (position != value && this._engineState == PlayerState.PLAYING)
				{
					// seek to new position
					this.pause();
					this._lastPosition = value;
					this.play();
				}else if(position != value && this._engineState == PlayerState.PAUSE){
					this._lastPosition = value;
				}
			}
			public function get state():PlayerState{
					var state:PlayerState = new PlayerState();
					
					if(this._currentListIndex == -1 || this._currentSongIndex == -1){
						//default states
					}else{
						state.currentPlayList = this._playLists[this._currentListIndex] as PlayList;
						//if(this._currentSongIndex>state.currentPlayList.items.length-1){
						//	state.currentSong = null;
						//}else
						try{
							state.currentSong = state.currentPlayList.items[this._currentSongIndex] as Song;
						}catch(ex:Error){
							state.currentSong = null;
							trace(ex.message);
						}
							
						if(this._currentSongIndex+1 > state.currentPlayList.items.length-1){
							state.canNextSong = false;	
						}else{
							state.canNextSong = true;	
						}
						if(this._currentSongIndex-1 < 0){
							state.canPrevSong = false;	
						}else{
							state.canPrevSong = true;	
						}	
					}
					
					state.engineState = this._engineState;
					return state;
			}
			
			public function get timeState():PlayerTimeState{
				var state:PlayerTimeState = new PlayerTimeState();
				state.currentPosition = this._channel?this._channel.position:0;
				state.duration = this._engine?this._engine.length:0;
				return state;
		
			}
			
			public function appendPlayList(list:PlayList):void{
				this._playLists.addItem(list);
				if(this._currentListIndex == -1)
					this._currentListIndex = 0;
				
				var evt:PlayerEvent = new PlayerEvent(PlayerEvent.PlayListsChange,this.state);
				evt.playerTimeState = this.timeState;
				dispatchEvent(evt);
				
			}
			
			public function appendSong(song:Song):void{
				if(!(this._playLists.length>0)){
					throw('There is no play list exists!');
				}
				if(this._currentListIndex == -1)
					this._currentListIndex = 0;
				var curList:PlayList = this._playLists[this._currentListIndex] as PlayList;
				curList.append(song);
				
				var evt:PlayerEvent = new PlayerEvent(PlayerEvent.PlayListsChange,this.state);
				evt.playerTimeState = this.timeState;
				dispatchEvent(evt);
				
			}
			
			public function removeSong(song:Song):void{
				if(!(this._playLists.length>0)){
					throw('There is no play list exists!');
				}
				
				//if(song == this.state.currentSong)
				//	this.stop();
				
				var curList:PlayList = this._playLists[this._currentListIndex] as PlayList;
				var delTarIdx:int = -1;
				for(var i:int = 0;i<curList.items.length;i++){
					if(song == Song(curList.items[i])){
						delTarIdx = i;
					}
				}
				if(this._currentSongIndex > delTarIdx)
					this._currentSongIndex--;
				else if(this._currentSongIndex < delTarIdx)
					;
				else{
					this.stop();
					this._currentSongIndex = 0;
				}
				curList.remove(song);
				
				var evt:PlayerEvent = new PlayerEvent(PlayerEvent.PlayListsChange,this.state);
				evt.playerTimeState = this.timeState;
				dispatchEvent(evt);
				
			}
			
			private function _playList(index:int):void{
				if(index>this._playLists.length-1 || index < 0){
					throw('There is no play list at index: '+index);	
				}
				this._currentListIndex = index;
				this._currentSongIndex = 0;
				this.stop();
				this.play();
			}
			
			public function playList(listID:String):void{
				var idx:int = -1;
				for(var i:int=0;i<this._playLists.length;i++){
					if((this._playLists[i] as PlayList).listID == listID){
						idx = i;
						break;
					}
				}
				if(idx != -1)
					this._playList(idx);
				else
					//this.stop();
					throw('There is no spec list ,listID='+listID);
			}
			
			public function play():void{
				
				if(this._engineState == PlayerState.PAUSE){
					this._engineState = PlayerState.PLAYING;
					this._channel = this._engine.play(this._lastPosition);
					this._channel.soundTransform = new SoundTransform(this.volume);
					this._channel.addEventListener(Event.SOUND_COMPLETE,this._soundCompleteEvent);
					var evt:PlayerEvent = new PlayerEvent(PlayerEvent.MediaOpen,this.state);
					evt.playerTimeState = this.timeState;
					dispatchEvent(evt);
				}else if(this._engineState == PlayerState.STOP){
					if(!(this._playLists.length>0)){
						throw('There is no play list exists!');
					}
					this._engine = new Sound();
					this._engine.addEventListener(Event.COMPLETE, this._engineEvent);
					this._engine.addEventListener(IOErrorEvent.IO_ERROR, this._engineEvent);
					this._engine.addEventListener(Event.OPEN, this._engineEvent);
					this._engine.addEventListener(ProgressEvent.PROGRESS, this._engineEvent);
					
					if(this._currentListIndex == -1)
						this._currentListIndex = 0;
					if(this._currentSongIndex == -1)
						this._currentSongIndex = 0;
			
					var curList:PlayList = this._playLists[this._currentListIndex] as PlayList;
					if(this._playLists.length!=0&&curList.items.length!=0){
							var curSong:Song = curList.items[this._currentSongIndex] as Song;
							this._engine.load(new URLRequest(curSong.url));
							this._channel = this._engine.play();
							this._channel.soundTransform = new SoundTransform(this.volume);
							this._channel.addEventListener(Event.SOUND_COMPLETE,this._soundCompleteEvent);
							
					}else{
						this.stop();
						//throw('There is no songs in play list for playing');
					}
					this._lastPosition = 0;
				}
				this._engineState = PlayerState.PLAYING;
				
				if(this._tickTimer == null){
					this._tickTimer = new Timer(1000);
					this._tickTimer.addEventListener(TimerEvent.TIMER,this._timeTickEvent);
					this._tickTimer.start();
				}
				
				
			}
	
			public function stop():void{
				if(this._tickTimer != null){
						this._tickTimer.stop();
					}
				this._tickTimer = null;
				if(this._channel != null){
					try{
						this._channel.stop();
					}catch(ex:Error){
						trace(ex.message);
					}
				}
				this._channel = null;
				
				try{
					this._engine.close();
				}catch(ex:Error){
					trace(ex.message);
				}
				
				this._engineState = PlayerState.STOP;
				var evt:PlayerEvent = new PlayerEvent(PlayerEvent.Stop,this.state);
				evt.playerTimeState = this.timeState;
				dispatchEvent(evt);
				
			}
			
			public function pause():void{
				if(this._tickTimer != null){
						this._tickTimer.stop();
				}
				this._tickTimer = null;
				this._channel.stop();
				this._engineState = PlayerState.PAUSE;
				var evt:PlayerEvent = new PlayerEvent(PlayerEvent.Pause,this.state);
				evt.playerTimeState = this.timeState;
				dispatchEvent(evt);
			}
			public function goNextSong():void{
				if(!(this._playLists.length>0)){
					throw('There is no play list exists!');
				}
				if(this._currentListIndex == -1)
					this._currentListIndex = 0;
				if(this._currentSongIndex == -1)
					this._currentListIndex = 0;
		
				var curList:PlayList = this._playLists[this._currentListIndex] as PlayList;
				if(this._currentSongIndex+1 > curList.items.length -1){
					throw('Could not go next,reach the tail of the playlist');
				}
				this.stop();
		
				this._currentSongIndex++;
			
				this.play();
			}

			public function goPrevSong():void{
				if(!(this._playLists.length>0)){
					throw('There is no play list exists!');
				}
				if(this._currentListIndex == -1)
					this._currentListIndex = 0;
				if(this._currentSongIndex == -1)
					this._currentListIndex = 0;
		
				if(this._currentSongIndex-1 < 0){
					throw('Could not go prev,reach the head of the playlist');
				}
				this.stop();
		
				this._currentSongIndex--;
		
				this.play();
			}
			
			public function goSongByIndex(index:int):void{
				if(!(this._playLists.length>0)){
					throw('There is no play list exists!');
				}
				if(this._currentListIndex == -1)
					this._currentListIndex = 0;
				if(this._currentSongIndex == -1)
					this._currentListIndex = 0;
				
				var curList:PlayList = this._playLists[this._currentListIndex] as PlayList;
				
				if(index<0||index > curList.items.length -1){
					throw('Index out of bound in current play list');	
				}
				this.stop();
				this._currentSongIndex = index;
				trace('gosongbyidx:'+this._currentSongIndex);

				this.play();
			}
	
			private function _engineEvent(event:Event):void{
				if(event.type == IOErrorEvent.IO_ERROR)
					trace('engine event:'+event);
				if(event.type == Event.OPEN){
					var evt:PlayerEvent = new PlayerEvent(PlayerEvent.MediaOpen,this.state);
					evt.playerTimeState = this.timeState;
					dispatchEvent(evt);
					
				}
				if(event.type == ProgressEvent.PROGRESS){
					var evtProgress:ProgressEvent = event.clone() as ProgressEvent;
					//def event type string in PlayerEvent's const 
					//evt.type = PlayerEvent.Bufferring;
					dispatchEvent(evtProgress);
				}
				
			}
			
			private function _soundCompleteEvent(event:Event):void{
					
					if(this._tickTimer != null){
							this._tickTimer.stop();
					}
					this._tickTimer = null;
					this._engineState = PlayerState.STOP;
					var evt:PlayerEvent = new PlayerEvent(PlayerEvent.MediaEnd,this.state);
					evt.playerTimeState = this.timeState;
					dispatchEvent(evt);

				
			}
			
			private function _timeTickEvent(event:TimerEvent):void{
				var evt:PlayerEvent = new PlayerEvent(PlayerEvent.TimeTick,this.state);
				evt.playerTimeState = this.timeState;
				this._lastPosition = evt.playerTimeState.currentPosition;
				dispatchEvent(evt);
			}
	
	}
}